home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------*/
- /* Frames.c Example of how to use coordinte frames in a 3d world
- /* Jaime del Palacio CIS: 73072,3134 (c) Copyright 1996
- /* http://ourworld.compuserve.com/homepages/jdp_Site/
- /* This example shows how to use the notion of frames of reference
- /* to simplify the transformations between OCS, WCS, CCS, etc.
- /* also shows how to specify rotations in local axis to have
- /* a coherent rotation direction always
- /* This example is that _an example_ it is not optimized nor the best
- /* possible way to implement it;
-
- /* This program was compiled using BC 4.5 in small memory model
- /* and should work on any vga.
- /*----------------------------------------------------------------------*/
- /* include files */
- #include <MATH.h>
- #include <STDLIB.h>
- #include <STDIO.h>
- #include <CONIO.h>
- #include <MEM.h>
-
- /*----------------------------------------------------------------------*/
- /* type definition */
-
- #define BYTE unsigned char
-
- typedef enum { FALSE, TRUE } BOOL;
- // Simple 3D vector structure
- typedef struct
- {
- float x,y,z;
- } tVector;
-
- // a 3x3 rotation matrix
- typedef struct
- {
- float element[3][3];
- } t3x3Mat;
-
- // a frame description scructure
- typedef struct
- {
- tVector Translation;
- t3x3Mat Rotation;
- } tFrame;
-
- typedef struct
- {
- int x,y;
- } tScreen;
-
-
-
- /*----------------------------------------------------------------------*/
- /* Constants & definitions */
-
- #define NumVertices 4
-
- const tVector unitX = { 1, 0, 0 };
- const tVector unitY = { 0, 1, 0 };
- const tVector unitZ = { 0, 0, 1 };
- const tVector zero = { 0, 0, 0 };
-
- const t3x3Mat identMatrix = { 1.f,0.f,0.f ,
- 0.f,1.f,0.f ,
- 0.f,0.f,1.f };
-
- static const float viewDist = 128; // distance of the viewer
-
- const tVector planeData[NumVertices] = { {0,0,0}, {1,0,0}, {0,1,0}, {0,0,1}};
-
- const float minZ = -1.f;
-
- // screen constants for the mode
- #define SCREEN_CENTERX 160
- #define SCREEN_CENTERY 100
-
-
- /*----------------------------------------------------------------------*/
- /* Global variables */
-
- unsigned char far *SCREEN = (unsigned char far*)0xA0000000L;
- float sinTbl[360];
- float cosTbl[360];
-
- tFrame *frames[2];
- int curFrame;
- int local;
-
- tVector v[NumVertices];
- tScreen s[NumVertices];
-
- /*----------------------------------------------------------------------*/
- /* Prototypes */
-
- /*----------------------------------------------------------------------*/
- /* Code */
-
-
- /*-------------------------------------*/
- /* Graphics functions */
-
- // SetMode: Set the video mode to 'mode'
- void SetMode( BYTE mode )
- {
- asm
- {
- mov ax,0
- mov al,mode
- int 0x10
- }
- } // SetMode
-
- // Putpixel: draws a pixel of at x,y of SCREEN (320x200) assumed
- void PutPixel( unsigned char far *SCREEN, int x, int y, BYTE color )
- {
- SCREEN[(y<<8)+(y<<6)+x] = color;
- } // PutPixel
-
- void ClearScreen( BYTE far * SCREEN )
- {
- _fmemset( SCREEN, 0, (size_t)64000L );
- }
-
- void Line(unsigned char far *Screen, int X1, int Y1, int X2,int Y2, int Color)
- {
- unsigned int X_Unit, Y_Unit, XDiff, YDiff, Error;
- asm{
- CLD
- Mov Ax, WORD [Screen+2]
- Mov ES, Ax
- Mov Ax, Y1
- Mov Dx, 320
- Mul Dx
- Add Ax, X1
- Add Ax, WORD [Screen]
- Mov Bx, Ax
- }
- InitL :
- asm{
- Mov Dx, Color
- Mov Error, 0
- Mov Ax, Y2
- Sub Ax, Y1
- JNS YPos
- Mov Y_Unit, -320
- Neg Ax
- Mov YDiff, Ax
- Jmp Next
- }
- YPos :
- asm{
- Mov Y_Unit, 320
- Mov YDiff, Ax
- }
- Next :
- asm {
- Mov Ax, X2
- Sub Ax, X1
- JNS XPos
- Mov X_Unit, -1
- Neg Ax
- Mov XDiff, Ax
- Jmp Next2
- }
- XPos :
- asm{
- Mov X_Unit, 1
- Mov XDiff, Ax
- }
- Next2 :
- asm{
- Cmp Ax, YDiff
- JC YLine
- Jmp XLine
- }
- XLine :
- asm{
- Mov Cx,XDiff
- Inc Cx
- }
- XLine1:
- asm{
- Mov ES:[Bx], Dl
- Add Bx, X_Unit
- Mov Ax, Error
- Add Ax, YDiff
- Mov Error, Ax
- Sub Ax, XDiff
- JC XLine2
- Mov Error, Ax
- Add Bx, Y_Unit
- }
- XLine2:
- asm{
- Loop XLine1
- Jmp LDone
- }
- YLine :
- asm{
- Mov Cx, YDiff
- Inc Cx
- }
- YLine1:
- asm{
- Mov Es:[Bx], Dl
- Add Bx, Y_Unit
- Mov Ax, Error
- Add Ax, XDiff
- Mov Error, Ax
- Sub Ax, YDiff
- JC YLine2
- Mov Error, Ax
- Add Bx, X_Unit
- }
- YLine2:
- asm{
- Loop YLine1
- }
- LDone :
- asm{
- And Ax,Ax
- }
- }
-
-
- tScreen ToScreen( tVector v )
- {
- tScreen s;
- float projK;
- s.x = 0; s.y = 0;
- if ( v.z >= minZ )
- return s;
- // perspective proyection using right handed coord. system
- projK = (float)viewDist / v.z;
- s.x = (int)(-v.x * projK) + SCREEN_CENTERX;
- s.y = (int)((v.y * projK)) + SCREEN_CENTERY;
-
- // simple clipping here
- if ( s.x >= 320 )
- s.x = 319;
- if ( s.x < 0 )
- s.x = 0;
- if ( s.y >= 200 )
- s.y = 199;
- if ( s.y < 0 )
- s.y = 0;
- // done
- return s;
- } // ToScreen
-
- /*-------------------------------------*/
- /* Math & table funtions */
- /* some math functions for matrices and vectors */
-
-
- #define RAD(x) ((float)(x)*M_PI / 180)
-
-
- void MakeSinCosTables( void )
- {
- int j;
- for (j=0; j<360; j++)
- {
- sinTbl[j] = sin(RAD(j));
- cosTbl[j] = cos(RAD(j));
- }
- } // MakeSinCosTalbes
-
-
- tVector vector( float x, float y, float z )
- {
- tVector v;
- v.x = x;
- v.y = y;
- v.z = z;
- return v;
- }
-
- float Len( tVector v )
- {
- return ( sqrt(v.x*v.x + v.y*v.y + v.z*v.z) );
-
- } /* Len */
-
-
- tVector Normalize( tVector v )
- {
- float a;
- if( (a = Len(v)) == 0)
- return vector(0.f,0.f,0.f);
- return vector( v.x/a, v.y/a, v.z/a );
- } /* Normalize */
-
-
- // adds to vectors and puts the result in v1
- tVector AddVector( tVector v1, tVector v2 )
- {
- v1.x += v2.x;
- v1.y += v2.y;
- v1.z += v2.z;
- // done
- return v1;
- }
-
- /* --------------------------------------------------------- */
- /* operator *(t3x3Mat, tVector): multiply a vector times a matrix
- /* --------------------------------------------------------- */
- tVector PreMatMul( tVector v, t3x3Mat mat )
- {
- tVector r;
- r.x = v.x * mat.element[0][0] + v.y * mat.element[1][0] + v.z * mat.element[2][0];
- r.y = v.x * mat.element[0][1] + v.y * mat.element[1][1] + v.z * mat.element[2][1];
- r.z = v.x * mat.element[0][2] + v.y * mat.element[1][2] + v.z * mat.element[2][2];
- return r;
- }
-
- /* --------------------------------------------------------- */
- /* operator *(t3x3Mat, tVector): multiply a matrix times a vector
- /* --------------------------------------------------------- */
- tVector PostMatMul( t3x3Mat mat, tVector v )
- {
- tVector r;
- r.x = mat.element[0][0] * v.x + mat.element[0][1]* v.y + mat.element[0][2] * v.z;
- r.y = mat.element[1][0] * v.x + mat.element[1][1]* v.y + mat.element[1][2] * v.z;
- r.z = mat.element[2][0] * v.x + mat.element[2][1]* v.y + mat.element[2][2] * v.z;
- return r;
- }
-
- /* --------------------------------------------------------- */
- /* operator *(t3x3Mat, t3x3Mat): multiply two matrices
- /* --------------------------------------------------------- */
- t3x3Mat MatMul(t3x3Mat m1, t3x3Mat m2 )
- {
- t3x3Mat r;
- int i,j,k;
- for( i=0; i<3; i++ )
- for( j=0; j<3; j++ )
- {
- r.element[i][j] = 0.f;
- for( k=0; k<3; k++ )
- r.element[i][j] += m1.element[i][k] * m2.element[k][j];
- }
- return r;
- };
-
-
-
- // newFrame: creates a new frame aligned with the world and at the origin
- tFrame *NewFrame( void )
- {
- tFrame *f;
-
- // get some memory
- if ((f = (tFrame*)malloc(sizeof( tFrame) ))==NULL)
- return NULL;
- // intialize the frame aligned with the WCS
- f->Rotation = identMatrix;
- // translate the frame
- f->Translation = zero;
-
- return f;
- } // NewFrame
-
- /*-------------------------------------*/
- /* 3D funtions */
-
- t3x3Mat YawMat( int deg )
- {
- t3x3Mat r = identMatrix;
- float c = cosTbl[deg];
- float s = sinTbl[deg];
- r.element[0][0] = c;
- r.element[0][2] = s;
- r.element[2][0] = -s;
- r.element[2][2] = c;
- return r;
- };
-
-
- t3x3Mat PitchMat( int deg )
- {
- t3x3Mat r = identMatrix;
- float c = cosTbl[deg];
- float s = sinTbl[deg];
- r.element[1][1] = c;
- r.element[1][2] = -s;
- r.element[2][1] = s;
- r.element[2][2] = c;
- return r;
- };
-
- t3x3Mat RollMat( int deg )
- {
- t3x3Mat r = identMatrix;
- float c = cosTbl[deg];
- float s = sinTbl[deg];
- r.element[0][0] = c;
- r.element[0][1] = -s;
- r.element[1][0] = s;
- r.element[1][1] = c;
- return r;
- };
-
-
-
-
-
- // Transform from local frame coordinates to world coordinates
- tVector ToGlobal( tFrame f, tVector v )
- {
- // apply rotation
- v = PostMatMul( f.Rotation, v );
- // translate
- v.x += f.Translation.x;
- v.y += f.Translation.y;
- v.z += f.Translation.z;
- // done
- return v;
- } // ToGlobal
-
- // Transform from World coordinates to local frame coordinates
- tVector ToLocal( tFrame f, tVector v )
- {
- // translate
- v.x -= f.Translation.x;
- v.y -= f.Translation.y;
- v.z -= f.Translation.z;
- // apply rotation
- v = PreMatMul( v, f.Rotation );
- // done
- return v;
- } // ToLocal
-
- // Transform just the direction part from local to world coords.
- tVector DirToGlobal( tFrame f, tVector v )
- {
- v = PostMatMul( f.Rotation, v );
- return v;
- } // DirToGlobal
-
- // Transform just the direction part from world to local coords.
- tVector DirToLocal( tFrame f , tVector v )
- {
- v = PreMatMul( v, f.Rotation );
- return v;
- } // DirToGlobal
-
-
- void RotateFrameGlobal( tFrame *f, char axis, int deg )
- {
-
- t3x3Mat newRotation;
-
- // adjust degrees to the tables
- if ( deg > 360 )
- deg = deg % 360;
- if ( deg < 0 )
- deg = ( deg + 360 ) % 360;
-
-
- switch( axis )
- {
- case 'x':
- newRotation = PitchMat( deg );
- break;
- case 'y':
- newRotation = YawMat( deg );
- break;
- case 'z':
- newRotation = RollMat( deg );
- break;
- default:
- newRotation = identMatrix;
- break;
- }
- // concatenate with the old rotation
- f->Rotation = MatMul( newRotation, f->Rotation );
- // done
- } // RotateFrame
-
-
- // rotate a frame by 'deg' degrees arround and arbitrary
- // axis defined by the vector v
- void RotateFrameLocal( tFrame* f, char ax, int deg )
- {
- tVector axis;
- float s,c,k;
- t3x3Mat mat;
-
- switch( ax )
- {
- case 'x':
- axis = DirToGlobal( *f, unitX );
- break;
- case 'y':
- axis = DirToGlobal( *f, unitY );
- break;
- case 'z':
- axis = DirToGlobal( *f, unitZ );
- break;
- default:
- return;
- }
-
- // axis most be a unitary vector
- axis = Normalize( axis );
-
- // wrap arround
- if ( deg > 360 )
- deg = deg % 360;
- if ( deg < 0 )
- deg = ( deg + 360 ) % 360;
-
- // now compute the general rotation matrix
- s = (deg>=0?sinTbl[deg]:-sinTbl[deg]);
- c = (deg>=0?cosTbl[deg]:-cosTbl[deg]);
- k = 1.f-c;
-
- mat.element[0][0] = k*axis.x*axis.x + c;
- mat.element[0][1] = k*axis.x*axis.y-s*axis.z;
- mat.element[0][2] = k*axis.x*axis.z + s*axis.y;
- mat.element[1][0] = k*axis.x*axis.y + s*axis.z;
- mat.element[1][1] = k*axis.y*axis.y + c;
- mat.element[1][2] = k*axis.y*axis.z - s*axis.x;
- mat.element[2][0] = k*axis.x*axis.z - s*axis.y;
- mat.element[2][1] = k*axis.y*axis.z + s*axis.x;
- mat.element[2][2] = k*axis.z*axis.z + c;
- // concatenate this rotation with the current matrix
- f->Rotation = MatMul( mat , f->Rotation );
-
- // done
- return;
-
- }
-
-
- void TranslateFrame( tFrame *f, float X, float Y, float Z )
- {
- f->Translation.x += X;
- f->Translation.y += Y;
- f->Translation.z += Z;
- } // TranslateFrame
-
-
- /*-------------------------------------*/
- /* Others */
-
- // transfor all vertices to screen pixels
- void RenderScreen( BYTE far *SCREEN )
- {
- int j;
-
- // copy all point to the temporal structure
- for (j=0; j<NumVertices; j++)
- {
- v[j].x = planeData[j].x;
- v[j].y = planeData[j].y;
- v[j].z = planeData[j].z;
- }
-
- for (j=0; j<NumVertices; j++)
- {
- // Transform vertices to Global frame
- v[j] = ToGlobal( *frames[0], v[j] );
- // Transform vertices to Camera frame
- v[j] = ToLocal( *frames[1], v[j] );
- // project vertices
- s[j] = ToScreen( v[j] );
- }
-
- // draw a frame shape 0-1, 0-2, 0-3
- Line( SCREEN, s[0].x, s[0].y, s[1].x, s[1].y, 10 );
- Line( SCREEN, s[0].x, s[0].y, s[2].x, s[2].y, 11 );
- Line( SCREEN, s[0].x, s[0].y, s[3].x, s[3].y, 12 );
-
-
- // render state text
- gotoxy(1,23);
- printf("currFrame: %s\nlocal: %d",( curFrame==0?"plane":"camera"),
- local );
-
- // done for this frame
- return;
-
- } // RenderScreen
-
- void InitAll( void )
- {
- // Make math LUTs
- MakeSinCosTables();
- //init frames
- frames[0] = NewFrame();
- TranslateFrame( frames[0], 0.f, 0.f, -3.f );
- frames[1] = NewFrame();
- // done
- return;
- } // InitAll
-
-
-
- void DoneAll( void )
- {
- free( frames[0] );
- free( frames[1] );
- // done
- return;
- }
-
-
- void SplashPlate( void )
- {
- clrscr();
- printf("Frame.exe Frames sample (c) Copyright 1996 Jaime del Palacio\n");
- printf("This program shows the concept of using frames of reference\n");
- printf("to simplify the management of 3d transformations.\n" );
- printf("It also shows how to rotates frames with resprect of their local axis\n");
- printf("to mantain visual coherence. This technique is very\n");
- printf("simple and clear (IMO) for use in any type of 3D engine.\n");
- printf("Warning: No clipping is implemented so don't go out of sight\n");
- printf("If you have any questions, comments or whatever, send me e-mail to\n");
- printf("Compuserve: 73072,3134\n");
- printf("Internet: 73072.3134@compuserve.com\n");
- printf("home page: http://ourworld.compuserve.com/homepages/jdp_Site/\n\n");
- printf("Usage:\n");
- printf("<Y> +Yaw <y> -Yaw\n");
- printf("<P> +Pitch <p> -Pitch\n");
- printf("<R> +Roll <r> -Roll\n");
- printf("<A> move in the +Z direction\n");
- printf("<Z> move in the -Z direction\n");
- printf("<F> change current frame\n");
- printf("<L> toggle local rotations on & off\n" );
- printf("<ESC> Quit\n");
- printf("Have fun!\n");
- printf("Press any key");
-
- getch();
-
- // done
- return;
- }
-
- void main(void)
- {
- char key;
- tVector deltaT;
- // number of degrees per arrow hit
- static int degPerStep = 3;
- local = 0;
-
- // print helpscreen first
- SplashPlate();
-
- // set up 320x200 256 colors
- SetMode( 0x13 );
-
- // Initialize all fremes and vertices
- InitAll();
- // main loop
- do
- {
- ClearScreen(SCREEN);
- RenderScreen(SCREEN);
- // process input and logic here
- key = getch();
- switch (key)
- {
- // yaw rotation
- case 'y':
- if( local )
- RotateFrameLocal( frames[curFrame], 'y', degPerStep );
- else
- RotateFrameGlobal( frames[curFrame], 'y', degPerStep );
- break;
- case 'Y':
- if( local )
- RotateFrameLocal( frames[curFrame], 'y', -degPerStep );
- else
- RotateFrameGlobal( frames[curFrame], 'y', -degPerStep );
- break;
- // pitch
- case 'p':
- if( local )
- RotateFrameLocal( frames[curFrame], 'x', degPerStep );
- else
- RotateFrameGlobal( frames[curFrame], 'x', degPerStep );
- break;
- case 'P':
- if( local )
- RotateFrameLocal( frames[curFrame], 'x', -degPerStep );
- else
- RotateFrameGlobal( frames[curFrame], 'x', -degPerStep );
- break;
- // roll
- case 'r':
- if( local )
- RotateFrameLocal( frames[curFrame], 'z', degPerStep );
- else
- RotateFrameGlobal( frames[curFrame], 'z', degPerStep );
- break;
- case 'R':
- if( local )
- RotateFrameLocal( frames[curFrame], 'z', -degPerStep );
- else
- RotateFrameGlobal( frames[curFrame], 'z', -degPerStep );
- break;
- // switch to next frame
- case 'f':
- case 'F':
- curFrame = (curFrame == 0)? 1 : 0;
- break;
- // translation of the local frame
- // a moves the frame in the local -Z direction
- case 'a':
- case 'A':
- if( local )
- deltaT = DirToGlobal( *frames[curFrame], unitZ );
- else
- deltaT = unitZ;
- TranslateFrame( frames[curFrame], -deltaT.x, -deltaT.y, -deltaT.z );
- break;
- // z moves the frame in the local Z direction
- case 'z':
- case 'Z':
- if( local )
- deltaT = DirToGlobal( *frames[curFrame], unitZ );
- else
- deltaT = unitZ;
- TranslateFrame( frames[curFrame], deltaT.x, deltaT.y, deltaT.z );
- break;
- case 'l':
- case 'L':
- local = local==0?1:0;
- break;
- };
- } while ( key != 27 );
- DoneAll();
- // set text mode
- SetMode( 0x03 );
- }
- /* eof : Frames.c */
-